---
title: 미들웨어
description: Astro에서 미들웨어를 사용하는 방법을 알아보세요.
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { Steps } from '@astrojs/starlight/components';
import Since from '~/components/Since.astro';

**미들웨어**를 사용하면 페이지나 엔드포인트가 렌더링될 때마다 요청과 응답을 가로채고 동작을 동적으로 추가할 수 있습니다. 이 렌더링은 사전 렌더링된 모든 페이지에 대해 빌드 시 발생하지만, 요청 시 렌더링된 페이지에 대해 경로가 요청될 때 발생하므로 [쿠키 및 헤더와 같은 추가 SSR 기능](/ko/guides/on-demand-rendering/)을 사용할 수 있습니다.

또한 미들웨어를 사용하면 모든 Astro 컴포넌트 및 API 엔드포인트에서 사용할 수 있는 `locals` 객체를 변경하여 엔드포인트와 페이지 전반에 걸쳐 요청별 정보를 설정하고 공유할 수 있습니다. 이 객체는 빌드 시 이 미들웨어가 실행되는 경우에도 사용할 수 있습니다.

## 사용 방법

<Steps>
1. `src/middleware.js|ts` 파일을 생성합니다. (또는 `src/middleware/index.js|ts` 파일을 생성할 수도 있습니다.)

2. 이 파일에서 [`context` 객체](#context-객체)와 `next()` 함수를 전달할 수 있는 [`onRequest()`](/ko/reference/modules/astro-middleware/#onrequest) 함수를 내보냅니다. 이는 기본 내보내기가 아니어야 합니다.

    ```js title="src/middleware.js"
    export function onRequest (context, next) {
        // 요청의 응답 데이터 가로채기
        // 선택적으로 `locals`의 속성을 수정합니다.
        context.locals.title = "새로운 제목";

        // Response 객체 또는 `next()` 함수의 호출 결과를 반환합니다.
        return next();
    };
    ```

3. `.astro` 파일에서 `Astro.locals`를 통해 응답 데이터를 사용합니다.

    ```astro title="src/components/Component.astro"
    ---
    const data = Astro.locals;
    ---
    <h1>{data.title}</h1>
    <p>{data.property} 속성은 미들웨어로부터 전달되었습니다.</p>
    ```
</Steps>

### `context` 객체

[`context`](/ko/reference/api-reference/) 객체에는 렌더링 프로세스 중 다른 미들웨어, API 경로 및 `.astro` 경로에 사용할 수 있는 정보가 포함되어 있습니다.

이는 `onRequest()`에 전달되는 선택적 인수로, 렌더링 중 공유할 추가 속성과 `locals` 객체를 포함할 수 있습니다. 예를 들어, `context` 객체에는 인증에 사용되는 쿠키가 포함될 수 있습니다.

### `context.locals`에 데이터 저장

`context.locals`는 미들웨어에서 조작할 수 있는 객체입니다.

이 `locals` 객체는 요청 처리 프로세스를 통해 전달되며 [`APIContext`](/ko/reference/api-reference/#locals) 및 [`AstroGlobal`](/ko/reference/api-reference/#locals)에 대한 속성으로 사용할 수 있습니다. 이를 통해 미들웨어, API 경로 및 `.astro` 페이지 간에 데이터를 공유할 수 있습니다. 이는 렌더링 단계 전반에 걸쳐 사용자 데이터와 같은 요청별 데이터를 저장하는 데 유용합니다.

:::tip[통합 속성]
[통합](/ko/guides/integrations-guide/)은 `locals` 객체를 통해 속성을 설정하고 기능을 제공할 수 있습니다. 통합을 사용하는 경우, 문서를 통해 해당 속성을 재정의하거나 불필요한 작업을 수행하고 있지 않은지 확인하세요.
:::

`locals`에는 문자열, 숫자, 심지어 함수나 맵과 같은 모든 타입의 데이터를 저장할 수 있습니다.

```js title="src/middleware.js"
export function onRequest (context, next) {
    // 요청의 응답 데이터 가로채기
    // 선택적으로 `locals`의 속성을 수정합니다.
    context.locals.user.name = "John Wick";
    context.locals.welcomeTitle = () => {
        return "반가워요 " + locals.user.name;
    };

    // Response 객체 또는 `next()` 함수의 호출 결과를 반환합니다.
    return next();
};
```

그러면 `Astro.locals`를 통해 `.astro` 파일에서 이 정보를 사용할 수 있습니다.

```astro title="src/pages/orders.astro"
---
const title = Astro.locals.welcomeTitle();
const orders = Array.from(Astro.locals.orders.entries());
const data = Astro.locals;
---
<h1>{title}</h1>
<p>이 {data.property} 속성은 미들웨어로부터 전달되었습니다.</p>
<ul>
    {orders.map(order => {
        return <li>{/* 각각의 order에 대해 해야 할 작업 */}</li>;
    })}
</ul>
```

`locals`는 단일 Astro 경로 내에서 살고 죽는 객체입니다. 경로 페이지가 렌더링되면, 기존 `locals`가 제거되고 새로운 `locals`가 생성됩니다. 그러므로 여러 페이지 요청에 걸쳐 지속되어야 하는 정보는 다른 곳에 저장되어야 합니다.

:::note
`locals` 값은 런타임에 재정의될 수 없습니다. 그렇게 하면 사용자가 저장한 모든 정보가 지워질 위험이 있기 때문입니다. Astro는 검사를 수행하고, `locals`가 재정의되면 오류를 발생시킵니다.
:::

## 예: 민감한 정보 수정

아래 예에서는 페이지에서 수정된 HTML을 렌더링할 수 있도록 미들웨어를 사용하여 "비밀 정보"라는 단어를 "수정됨"으로 대체합니다.

```js title="src/middleware.js"
export const onRequest = async (context, next) => {
    const response = await next();
    const html = await response.text();
    const redactedHtml = html.replaceAll("비밀 정보", "수정됨");
    
    return new Response(redactedHtml, {
        status: 200,
        headers: response.headers
    });
};
```

## 미들웨어 타입

타입 안전성을 활용하기 위해 `defineMiddleware()` 유틸리티 함수를 가져와 사용할 수 있습니다.

```ts
// src/middleware.ts
import { defineMiddleware } from "astro:middleware";

// `context`와 `next`가 자동으로 입력됩니다.
export const onRequest = defineMiddleware((context, next) => {

});
```

대신 JsDoc을 사용하여 타입 안전성을 활용하는 경우 `MiddlewareHandler`를 사용할 수 있습니다.

```js
// src/middleware.js
/**
 * @type {import("astro").MiddlewareHandler}
 */
// `context`와 `next`가 자동으로 입력됩니다.
export const onRequest = (context, next) => {

};
```

`.astro` 파일과 미들웨어 코드에서 자동 완성 기능을 제공하는 `Astro.locals`의 데이터에 타입 정보를 제공하려면 `env.d.ts` 파일에 전역 네임스페이스를 선언하여 [전역 타입을 확장](/ko/guides/typescript/#전역-타입-확장하기)하세요.

```ts title="src/env.d.ts"
type User = {
  id: number;
  name: string;
};

declare namespace App {
  interface Locals {
    user: User;
    welcomeTitle: () => string;
    orders: Map<string, object>;
    session: import("./lib/server/session").Session | null;
  }
}
```

그러면 미들웨어 파일에서 자동 완성 및 타입 안전성을 활용할 수 있습니다.

## 미들웨어 체이닝

[`sequence()`](/ko/reference/modules/astro-middleware/#sequence)를 사용하여 여러 미들웨어를 지정된 순서로 결합할 수 있습니다.

```js title="src/middleware.js"
import { sequence } from "astro:middleware";

async function validation(_, next) {
    console.log("validation 요청");
    const response = await next();
    console.log("validation 응답");
    return response;
}

async function auth(_, next) {
    console.log("auth 요청");
    const response = await next();
    console.log("auth 응답");
    return response;
}

async function greeting(_, next) {
    console.log("greeting 요청");
    const response = await next();
    console.log("greeting 응답");
    return response;
}

export const onRequest = sequence(validation, auth, greeting);
```

그러면 다음과 같은 순서대로 콘솔에 메시지가 출력됩니다.

```sh
validation 요청
auth 요청
greeting 요청
greeting 응답
auth 응답
validation 응답
```

## 리라이팅

<p><Since v="4.13.0" /></p>

`APIContext`는 `rewrite()`라는 메서드를 노출하는데, 이는 [Astro.rewrite](/ko/guides/routing/#url-재작성-rewrites)와 동일한 방식으로 작동합니다.

방문자를 새 페이지로 [리디렉션](/ko/guides/routing/#동적-리디렉션)하지 않고 다른 페이지의 콘텐츠를 표시하려면 미들웨어에서 `context.rewrite()`를 사용하세요. 이렇게 하면 새 렌더링 단계가 트리거되어 모든 미들웨어가 다시 실행됩니다.

```js title="src/middleware.js"
import { isLoggedIn } from "~/auth.js"
export function onRequest (context, next) {
  if (!isLoggedIn(context)) {
    // 사용자가 로그인하지 않은 경우 `/login` 경로를 렌더링하도록 요청을 업데이트하고
    // 로그인 성공 후 사용자에게 전송할 위치를 나타내는 헤더를 추가합니다.
    // 미들웨어를 다시 실행합니다.
    return context.rewrite(new Request("/login", {
      headers: {
        "x-redirect-to": context.url.pathname
      }
    }));
  }

  return next();
};
```

또한 `next()` 함수에 선택적 URL 경로 매개변수를 전달하여 새 렌더링 단계를 다시 트리거하지 않고 현재 `Request`를 리라이트할 수 있습니다. 리라이트 경로의 위치는 문자열, URL 또는 `Request`로 제공할 수 있습니다:

```js title="src/middleware.js"
import { isLoggedIn } from "~/auth.js"
export function onRequest (context, next) {
  if (!isLoggedIn(context)) {
    // 사용자가 로그인하지 않은 경우 `/login` 경로를 렌더링하도록 요청을 업데이트하고
    // 로그인 성공 후 사용자에게 전송할 위치를 나타내는 헤더를 추가합니다.
    // 다음 미들웨어에 새 `context`를 반환합니다.
    return next(new Request("/login", {
      headers: {
        "x-redirect-to": context.url.pathname
      }
    }));
  }

  return next();
};
```

`next()` 함수는 [`Astro.rewrite()` 함수](/ko/reference/api-reference/#rewrite)와 동일한 페이로드를 전달받습니다. 리라이트 경로의 위치는 문자열, URL 또는 `Request`로 제공할 수 있습니다.

[sequence()](#미들웨어-체이닝)를 통해 여러 미들웨어 함수가 체인으로 연결된 경우, `next()`에 경로를 제출하면 `Request`가 제자리에 리라이트되고 미들웨어가 다시 실행되지 않습니다. 체인의 다음 미들웨어 함수는 업데이트된 `context`와 함께 새 `Request`를 받게 됩니다:

이 시그니처로 `next()`를 호출하면 이전 `ctx.request`를 사용하여 새 `Request` 객체가 생성됩니다. 즉, 이 리라이트 전이나 후에 `Request.body`를 사용하려고 하면 런타임 오류가 발생합니다. 이 오류는 [HTML 양식을 사용하는 Astro 액션](/ko/guides/actions/#html-양식-액션에서-액션-호출)에서 자주 발생합니다. 이러한 경우 미들웨어를 대신 `Astro.rewrite()`를 사용하여 Astro 템플릿에서 리라이트를 처리하는 것이 좋습니다.

```js title="src/middleware.js"
// 현재 URL은 https://example.com/blog

// 첫 미들웨어 함수
async function first(context, next) {
  console.log(context.url.pathname) // "/blog"가 기록됩니다.
  // 새 경로와 홈페이지가 리라이트됩니다.
  // 다음 함수에 전달되는 업데이트된 `context`를 반환합니다.
  return next("/")
}

// 현재 URL은 여전히 https://example.com/blog

// 두 번째 미들웨어 함수
async function second(context, next) {
  // 업데이트된 `context`를 전달받습니다.
  console.log(context.url.pathname) // "/"가 기록됩니다.
  return next()
}

export const onRequest = sequence(first, second);
```

## 오류 페이지

미들웨어는 일치하는 경로를 찾을 수 없는 경우에도 주문형 렌더링된 모든 페이지에 대해 실행을 시도합니다. 여기에는 Astro의 기본 (비어있는) 404 페이지와 사용자 정의 404 페이지가 포함됩니다. 그러나 해당 코드의 실행 여부는 [어댑터](/ko/guides/on-demand-rendering/)에 따라 결정됩니다. 일부 어댑터는 플랫폼별 오류 페이지를 대신 제공할 수 있습니다.

또한 미들웨어 자체 실행 중에 서버 오류가 발생하지 않는 한, 미들웨어는 사용자 정의 500 페이지를 포함하여 500 오류 페이지를 제공하기 전에 실행을 시도합니다. 미들웨어가 성공적으로 실행되지 않으면 500 페이지를 렌더링하기 위해 `Astro.locals`에 액세스할 수 없습니다.
